package com.yicheng.statistics.actor.analysis

import akka.actor.ActorLogging
import akka.actor.Props
import com.yicheng.statistics.actor.SaveHtaData
import akka.actor.Actor
import akka.actor.ActorRef
import com.yicheng.statistics.common.HTADB
import com.yicheng.statistics.common.RTAModel
import com.yicheng.statistics.common.RTAModel.CustomArea
import com.yicheng.statistics.common.TimeUtils
import com.yicheng.statistics.repo.cassandra.DataInfoDB
import scala.util.Failure
import scala.util.Success
import com.yicheng.statistics.common.GisMapUtil
import com.yicheng.statistics.common.GisPoint
import java.util.Date
import com.yicheng.statistics.repo.RTAModel.AreaInout
import com.yicheng.statistics.common.RTAModel.VehicleInOutAreaSetting
import org.joda.time.DateTime
import com.yicheng.statistics.service.HTAService
import com.yicheng.statistics.repo.model.Data.DataInfo
import com.carcloud.gateway.bean.data.DataVehPos
import com.carcloud.gateway.bean.data.DataVehOverall
import com.yicheng.statistics.actor.AnalysisParser
import akka.actor.SupervisorStrategy.Stop


/*
 * 进出区域统计分析
 * 在限制时间内 进入区域超过时间阀值 统计报警
 */
class AreaInOut extends Actor with ActorLogging with AnalysisParser{
  
  val saveHtaDataActor:ActorRef = context.child("saveHtaData").
    getOrElse(context.actorOf(Props[SaveHtaData], "saveHtaData"))
  
   import scala.concurrent.ExecutionContext.Implicits.global
   type S = VehicleInOutAreaSetting
  def receive: Receive = {
    case (deviceId:String, deviceType:Int, analyseGroupId:Long) =>
      val inState : InAreaState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
      val outState : OutAreaState = OutAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
      val state:AreaState = AreaState(inState, outState, deviceId, deviceType)
      log.info("进出区域 deviceId "+deviceId)
      
      HTADB.getVehicleInOutAreaSettings(analyseGroupId).map(s => {
        println("inoutarea setting is "+s)
        parseOneDayDataInfo(deviceType, deviceId, state, s.headOption.get, Parser _)
      })
    case Stop =>
      //context.parent ! Stop
      log.info("areainout stop")
      context.actorSelection("/user/daemon") ! AreaStop
    case _ => log.info("AreaInOut")
    
      
  }
  
  /**
   * 
   */
  def Parser(state:AreaState, setting:VehicleInOutAreaSetting, dataInfos:Seq[DataInfo], isEnd:Boolean):AreaState = {
    
    val area:CustomArea = setting.coverageArea.get
    val inAreaF = getInArea(area)
    val deviceId = state.deviceId
    val deviceType = state.deviceType
    
    var inState : InAreaState = state.in//InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
    var outState : OutAreaState = state.out//OutAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
    log.info("get state "+state)
    dataInfos.map( di => {
      val posData: DataVehPos = di.data_vehicle.posData.get
      val overAllData: DataVehOverall = di.data_vehicle.overallData.get
      val lonlat = GisMapUtil.convert2baidu(posData.longitude.get, posData.latitude.get)//百度坐标系转换
      
      if( TimeUtils.inTimeRang(setting.startTime, setting.endTime, di.data_time)){//在需要计算的时间内
        val point = GisPoint(lonlat._1, lonlat._2)
        setting.alarmType match {
          case 1 =>//进区域报警
            if(inArea(point, area, inAreaF)){//在区域内
              log.info("point "+point +" in area "+area+" instate is "+inState)
              if( inState.startTime.isEmpty){//进入区域第一条数据记录
                inState = inState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2),
                    startMileage = Some(overAllData.currentMileage))
                log.debug("first inoutstate is "+inState)
              }else{
                if( TimeUtils.inTimeRang(inState.startTime.get, di.data_time, setting.timeThreshold) ){//达到报警条件
                  
                  if(inState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    inState = inState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    log.debug("AreaInOut Alarm content["+inState+"]")
                    
                  }else{
                    //记录报警的开始时间 地点 
                    inState = inState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      inTime = Some(TimeUtils.secondsBetweenTime(inState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
            }else{
              if( !inState.startTime.isEmpty && inState.alarm){//有报警 说明出去了区域 记录报警结束的时间地点
                inState = inState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - inState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }
          case 2 =>
            if( inArea(point,area, inAreaF)){//如果在区域内
              if( !outState.startTime.isEmpty && outState.alarm){//有报警 说明回到区域 记录报警结束的时间地点
                outState = outState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(outState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - outState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }else{//不在区域内
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
            }
          case 3 => //进出区域都要报警
            if( inArea(point,area, inAreaF)){//如果在区域内
              if( inState.startTime.isEmpty){//进入区域第一条数据记录
                inState = inState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2),
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.inTimeRang(inState.startTime.get, di.data_time, setting.timeThreshold) ){//达到报警条件
                  
                  if(inState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    inState = inState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    inState = inState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      inTime = Some(TimeUtils.secondsBetweenTime(inState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
              //###################################################
              
              if( !outState.startTime.isEmpty && outState.alarm){//有报警 说明回到区域 记录报警结束的时间地点
                outState = outState.copy(
                    alarm = false,
                    endTime = Some(di.data_time),
                    endLon = Some(lonlat._1),
                    endLat = Some(lonlat._2),
                    alarmTime = Some(TimeUtils.secondsBetweenTime(outState.alarmStartTime.get, di.data_time)),
                    driveMileage = Some(overAllData.currentMileage - outState.startMileage.get)
                    )
                    
                val areaInout = AreaInout(
                      deviceType,
                      deviceId,
                      inState.startTime.get,
                      inState.endTime.get,
                      inState.inTime.get.toInt,
                      inState.startLon,
                      inState.startLat,
                      inState.endLon,
                      inState.endLat,
                      Some(area.id.toInt),
                      area.name,
                      Some(setting.analyseGroupsID.toInt),
                      setting.name,
                      DateTime.now().toDate(),
                      intomileage = inState.driveMileage,
                      None
                    )
                saveHtaDataActor ! areaInout
                
                //清理状态数据 进行下次分析
                inState = InAreaState(false,None, None, None, None, None, None, None, None, None, None, None, None, None)
              }
            }else{//不在区域内
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
              
              //###################################
              
              if( outState.startTime.isEmpty){ //记录第一条离开的时间 地点 和里程
                outState = outState.copy(
                    startTime = Some(di.data_time), 
                    startLon = Some(lonlat._1), 
                    startLat = Some(lonlat._2), 
                    startMileage = Some(overAllData.currentMileage))
              }else{
                if( TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time) > setting.timeThreshold){//超过时间门限 开始记录报警
                  if(outState.alarm){//如果持续报警 计算报警的距离和位置
                    
                    outState = outState.copy(
                      endTime = Some(di.data_time),
                      endLon = Some(lonlat._1),
                      endLat = Some(lonlat._2),
                      alarmTime = Some(TimeUtils.secondsBetweenTime(inState.alarmStartTime.get, di.data_time)),
                      driveMileage = Some(overAllData.currentMileage - inState.startMileage.get))
                    
                  }else{
                    //记录报警的开始时间 地点 
                    outState = outState.copy(
                      alarm = true,
                      alarmStartTime = Some(di.data_time),
                      outTime = Some(TimeUtils.secondsBetweenTime(outState.startTime.get, di.data_time))
                    )
                    
                  }
                }
              }
            }
        }
      }
    })
    
    if( inState.alarm && isEnd){//如果当天最后一次还在报警 保存报警的
      log.debug("AreaInOut Alarm AllDay"+inState)
      val areaInout = AreaInout(
              deviceType,
              deviceId,
              inState.startTime.get,
              inState.endTime.get,
              inState.inTime.get.toInt,
              inState.startLon,
              inState.startLat,
              inState.endLon,
              inState.endLat,
              Some(area.id.toInt),
              area.name,
              Some(setting.analyseGroupsID.toInt),
              setting.name,
              DateTime.now().toDate(),
              intomileage = inState.driveMileage,
              None
            )
        saveHtaDataActor ! areaInout
    }
    
    if( outState.alarm && isEnd){//如果当天最后一次还在报警 保存报警的
      val areaInout = AreaInout(
              deviceType,
              deviceId,
              outState.startTime.get,
              outState.endTime.get,
              outState.outTime.get.toInt,
              outState.startLon,
              outState.startLat,
              outState.endLon,
              outState.endLat,
              Some(area.id.toInt),
              area.name,
              Some(setting.analyseGroupsID.toInt),
              setting.name,
              DateTime.now().toDate(),
              None,
              outmileage = outState.driveMileage
            )
        saveHtaDataActor ! areaInout
    }
    val areaState:AreaState = AreaState(inState, outState, deviceId, deviceType)
    areaState
  }
  
  def inArea(p:GisPoint,area:CustomArea, f: (GisPoint, CustomArea) => Boolean):Boolean = {
    f(p, area)
  }
  
  def inCircelArea(p:GisPoint, area:CustomArea):Boolean = {
    val radius:Float = area.radius.get
    val point = GisPoint(area.lat.get.toDouble, area.lon.get.toDouble)
    GisMapUtil.IsPointInCircle(point, List(p), radius)
  }
  
  def inRectArea(p:GisPoint, area:CustomArea):Boolean = {
    
    val gisPoints:List[GisPoint] = area.originlnglat.get.split(",").map{ x =>
      val point = x.split("\\|")
      GisPoint(point.head.toDouble, point.last.toDouble)
    }.toList
    GisMapUtil.IsPointInRegion(p, gisPoints)
  }
  
  def inPolygonArea(p:GisPoint, area:CustomArea):Boolean = {
    val gisPoints:List[GisPoint] = area.originlnglat.get.split(",").map{ x =>
      val point = x.split("\\|")
      GisPoint(point.head.toDouble, point.last.toDouble)
    }.toList
    
    GisMapUtil.IsPointInRegion(p, gisPoints)
  }
  
  def getInArea(area:CustomArea):(GisPoint, CustomArea) => Boolean = {
    val fun = area.drawingType.get match {
      case "circle" => inCircelArea _ 
      case "rect" => inRectArea _
      case "polygon" => inPolygonArea _
    }
    fun
  }
}

case class InAreaState(
    alarm:Boolean,
    startTime:Option[Date],//进入区域时间
    endTime:Option[Date],//离开区域时间
    inTime:Option[Long],//在区域内时间
    alarmStartTime:Option[Date],//报警开始时间
    alarmEndTime:Option[Date],//报警结束时间
    alarmTime:Option[Long],//持续报警时间
    startLon:Option[Double],//进入精度
    startLat:Option[Double],//进入纬度
    endLon:Option[Double],
    endLat:Option[Double],
    startMileage:Option[Int],
    endMileage:Option[Int],
    driveMileage:Option[Int]
    )
case class OutAreaState(
    alarm:Boolean,
    startTime:Option[Date],//离开区域时间
    endTime:Option[Date],//进入区域时间
    outTime:Option[Long],//在区域内时间
    alarmStartTime:Option[Date],//报警开始时间
    alarmEndTime:Option[Date],//报警结束时间
    alarmTime:Option[Long],//持续报警时间
    startLon:Option[Double],//出区域经度
    startLat:Option[Double],//出区域纬度
    endLon:Option[Double],
    endLat:Option[Double],
    startMileage:Option[Int],
    endMileage:Option[Int],
    driveMileage:Option[Int]
)

case class AreaState(in:InAreaState, out:OutAreaState, deviceId:String, deviceType:Int)
case class AreaStop()